# -*- coding: utf-8 -*-
"""
  # Профиллировщик DebugProfileTools #

  Author:  Aleksandr Dragunkin --<alexandr69@gmail.com>

  Created: 25.12.2018

  Назначение:  Сбор характеристик работы программы, таких как время выполнения
  отдельных фрагментов (обычно подпрограмм), число верно предсказанных условных
  переходов, число кэш-промахов и т. д.

  Создает: текстовый файл-отчет с укзанием адреса функции и модуля
  с его числом вызовов и затраченном времени на вызов и суммарное время вызовов.

  Картинку PNG с графом вызовов функций во время выполнения.


"""

try:
    import wingdbstub
except:
    pass

# импорт для профилирования
import os
import sys
import configparser
import cProfile
import subprocess
import pstats


from inspect import getfile

# -----------------


class MyProfile:

    def __init__(self, func):
        self.profile_filename = self.get_filename_for_profile(func)
        _base = self.profile_filename[:-4]
        self.res_filename = _base + 'res'
        self.png_filename = _base + 'png'
        self.sts_filename = _base + 'txt'

    def write_stats(self):
        with open(self.sts_filename, 'w') as stream:
            stats = pstats.Stats(self.profile_filename, stream=stream)
            stats.sort_stats('cumtime', 'calls').print_stats()

    @property
    def config(self):
        if not '_config' in self.__dict__():
            config = configparser.ConfigParser()
            config.read('dpt_config.ini')
            self._config = config
        return self._config

    @property
    def _graphvize_path(self):
        try:
            return self.config['Graphviz']['path']
        except:
            return r'c:\Program Files (x86)\Graphviz2.38\bin'

    @property
    def _appdata_path(self):
        try:
            return self.config['APPDATA']['path']
        except:
            return r'c:\PKM74\Bin\APP'

    def get_filename_for_profile(self, func):
        '''определяем имя файла для профилирования'''
        # дирректория для файлов профилирования
        # проверяем существует ли и создаем при отсутствии
        PROFDIR = os.path.join(self. _appdata_path, 'PROFILE')
        if not os.path.exists(PROFDIR):
            os.makedirs(PROFDIR)

        _l = getfile(func)
        _base_module_name = os.path.basename(_l).split('.')[0]

        _basename = _base_module_name + '_' + func.__name__
        profile_filename = _basename + '.prof'
        full_profile_filename = os.path.join(PROFDIR, profile_filename)
        i = 0
        while os.path.exists(full_profile_filename):
            i += 1
            profile_filename = _basename + '{}{}{}'.format('(', i, ")") + '.prof'
            full_profile_filename = os.path.join(PROFDIR, profile_filename)
        return full_profile_filename

    def exec_gprof(self):
        # Вызываем gprof2dot из текущего интерпретатора
        _p_gprof = os.path.join(os.path.dirname(sys.executable), 'Scripts')
        proc = subprocess.Popen([os.path.join(_p_gprof,
                                              'gprof2dot.exe'),
                                 '-o', self.res_filename, '-f', 'pstats', self.profile_filename],
                                shell=True, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
        _w = proc.stdout.readlines()
        return _w

    def exec_dot(self):
        p = self._graphvize_path
        if os.path.exists(p):
            proc = subprocess.Popen([os.path.join(self._graphvize_path,
                                                  'dot.exe'),
                                     '-Tpng', '-Tps', self.res_filename, '-o', self.png_filename
                                     ], shell=True, stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT)
            _w = proc.stdout.readlines()
            return _w


def profile(func):
    """Decorator for run function profile"""

    def wrapper(*args, **kwargs):
        my_profile = MyProfile(func)
        profiler = cProfile.Profile()
        result = profiler.runcall(func, *args, **kwargs)
        profiler.dump_stats(my_profile.profile_filename)

        # Вызываем gprof2dot из текущего интерпретатора
        if my_profile.exec_gprof():
            # сообщение об ошибке
            print(_w)
        else:
            # создаем файл с картинкой
            _w = my_profile.exec_dot()
        # текстовый файл со статистикой
        my_profile.write_stats()

        return result

    return wrapper
